#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <alsa/asoundlib.h>
#include <opus/opus.h>

#define DEFAULT_DURATION 6
#define MAX_DURATION 30

struct recorder {
    snd_pcm_t* device;
    int channels;
    int sample_rate;
    int frames_per_buffer;  // 320 @ 16kHz, 20ms
    OpusEncoder* encoder;
    opus_int16* buffer;
};

int recorder_create(struct recorder* rec, int sample_rate, int channels) {
    int err;
    snd_pcm_hw_params_t* params;

    rec->sample_rate = sample_rate;
    rec->channels = channels;
    rec->frames_per_buffer = (sample_rate * 20) / 1000; // 固定20ms

    if ((err = snd_pcm_open(&rec->device, "default", SND_PCM_STREAM_CAPTURE, 0)) < 0) {
        fprintf(stderr, "无法打开录音设备: %s\n", snd_strerror(err));
        return -1;
    }

    snd_pcm_hw_params_alloca(&params);
    snd_pcm_hw_params_any(rec->device, params);
    snd_pcm_hw_params_set_access(rec->device, params, SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_format(rec->device, params, SND_PCM_FORMAT_S16_LE);
    snd_pcm_hw_params_set_channels(rec->device, params, channels);
    unsigned int rate = sample_rate;
    snd_pcm_hw_params_set_rate_near(rec->device, params, &rate, 0);

    if ((err = snd_pcm_hw_params(rec->device, params)) < 0) {
        fprintf(stderr, "无法设置录音参数: %s\n", snd_strerror(err));
        snd_pcm_close(rec->device);
        return -1;
    }

    if ((err = snd_pcm_prepare(rec->device)) < 0) {
        fprintf(stderr, "无法准备录音设备: %s\n", snd_strerror(err));
        snd_pcm_close(rec->device);
        return -1;
    }

    int error;
    rec->encoder = opus_encoder_create(sample_rate, channels, OPUS_APPLICATION_VOIP, &error);
    if (!rec->encoder || error != OPUS_OK) {
        fprintf(stderr, "Opus 编码器创建失败: %s\n", opus_strerror(error));
        snd_pcm_close(rec->device);
        return -1;
    }

    // 推荐设置比特率（语音足够）
    opus_encoder_ctl(rec->encoder, OPUS_SET_BITRATE(24000));

    rec->buffer = malloc(sizeof(opus_int16) * rec->frames_per_buffer * channels);
    if (!rec->buffer) {
        fprintf(stderr, "内存分配失败\n");
        opus_encoder_destroy(rec->encoder);
        snd_pcm_close(rec->device);
        return -1;
    }

    return 0;
}

void recorder_destroy(struct recorder* rec) {
    free(rec->buffer);
    if (rec->encoder) opus_encoder_destroy(rec->encoder);
    if (rec->device) snd_pcm_close(rec->device);
    memset(rec, 0, sizeof(*rec));
}

int record_audio(struct recorder* rec, FILE* fp) {
    if (snd_pcm_readi(rec->device, rec->buffer, rec->frames_per_buffer) != rec->frames_per_buffer) {
        // 尝试恢复
        snd_pcm_recover(rec->device, -EPIPE, 0);
        return -1;
    }

    unsigned char encoded_data[1276];
    int len = opus_encode(rec->encoder, rec->buffer, rec->frames_per_buffer, encoded_data, sizeof(encoded_data));
    if (len <= 0) {
        // 静音或编码失败，跳过
        return 0;
    }

    int32_t packet_len = len;
    if (fwrite(&packet_len, sizeof(packet_len), 1, fp) != 1 ||
        fwrite(encoded_data, 1, len, fp) != (size_t)len) {
        perror("写入文件失败");
        return -1;
    }

    return len;
}

// 获取当前毫秒时间（用于精确计时）
long long get_time_ms() {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec * 1000LL + ts.tv_nsec / 1000000LL;
}

int main(int argc, char* argv[]) {
    int duration = DEFAULT_DURATION;
    if (argc > 1) {
        duration = atoi(argv[1]);
        if (duration <= 0 || duration > MAX_DURATION) {
            fprintf(stderr, "警告: 时长应为 1~%d 秒，使用默认 %d 秒。\n", MAX_DURATION, DEFAULT_DURATION);
            duration = DEFAULT_DURATION;
        }
    }

    struct recorder rec = {0};
    if (recorder_create(&rec, 16000, 1) != 0) {
        return EXIT_FAILURE;
    }

    FILE* fp = fopen("input.opus", "wb");
    if (!fp) {
        perror("无法创建 input.opus");
        recorder_destroy(&rec);
        return EXIT_FAILURE;
    }

    printf("[INFO] 开始录音 %d 秒...\n", duration);

    long long start_time = get_time_ms();
    long long end_time = start_time + duration * 1000;
    int frame_count = 0;

    while (get_time_ms() < end_time) {
        if (record_audio(&rec, fp) < 0) {
            break;
        }
        frame_count++;
        // 每帧约20ms，无需额外 sleep
    }

    fclose(fp);
    recorder_destroy(&rec);

    printf("[INFO] 录音结束，共 %d 帧（%.1f 秒）\n", frame_count, frame_count * 0.02);
    return EXIT_SUCCESS;
}
